package com.iflytek.cosmo.ocr.common.core;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;

import com.iflytek.cosmo.ocr.common.config.ArgusProperties;
import com.iflytek.cosmo.ocr.common.config.WorksProperties;
import com.iflytek.cosmo.ocr.common.constant.CommonConstant;
import com.iflytek.cosmo.ocr.common.constant.FileConstant;
import com.iflytek.cosmo.ocr.common.core.station.CompressStation;
import com.iflytek.cosmo.ocr.common.core.station.FormatStation;
import com.iflytek.cosmo.ocr.common.core.station.RecognizedStation;
import com.iflytek.cosmo.ocr.common.core.task.FormatTask;
import com.iflytek.cosmo.ocr.common.enums.FileStatusEnum;
import com.iflytek.cosmo.ocr.common.enums.ResultCodeEnum;
import com.iflytek.cosmo.ocr.common.exception.CommonException;
import com.iflytek.cosmo.ocr.common.util.FileUtil;
import com.iflytek.cosmo.ocr.common.util.ImageUtil;
import com.iflytek.cosmo.ocr.common.util.JSONUtil;
import com.iflytek.cosmo.ocr.model.domain.FileInfo;
import com.iflytek.cosmo.ocr.model.domain.MessageOutput;
import com.iflytek.cosmo.ocr.model.domain.State;
import com.iflytek.cosmo.ocr.service.ArgusPostService;
import com.iflytek.cosmo.ocr.service.FileService;
import org.apache.commons.io.IOUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.encryption.InvalidPasswordException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

/**
 * 任务调度系统
 */
public class TaskDispatchSystem {

    /**
     *
     */
    private static volatile TaskDispatchSystem _inst;

    private static  WorksProperties worksProperties;

    /**
     * 获取实例
     *
     * @return
     */
    public static TaskDispatchSystem getInstance(WorksProperties worksPropertie) {
        if (_inst == null) {
            synchronized (TaskDispatchSystem.class) {
                if (_inst == null) {
                    worksProperties = worksPropertie;
                    _inst = new TaskDispatchSystem();
                }
            }
        }
        return _inst;
    }

    /**
     * 缓存目录
     */
    private String tempDir;

    /**
     * 工作台
     */
    public FormatStation formatStation;

    public RecognizedStation recognizedStation;

    /**
     * 日志记录器
     */
    private Logger logger = LoggerFactory.getLogger(TaskDispatchSystem.class);

    /**
     * 调度系统
     */
    public TaskDispatchSystem() {
        formatStation = new FormatStation(worksProperties);
        CompressStation cStation = new CompressStation(worksProperties);
        recognizedStation = new RecognizedStation(worksProperties);
        cStation.setNextStation(recognizedStation);
        formatStation.setNextStation(cStation);
    }

    /**
     * 添加一个任务
     */
    public MessageOutput<String> addTask(String userId, String dirId, MultipartFile multipartFile, String ip) {

        // 校验上传文件的格式和大小
        MessageOutput<String> invalidMessage = checkUploadFileTypAndSize(multipartFile, userId);
        if (invalidMessage != null) {
            return invalidMessage;
        }

        FileInfo fileInfo = null;
        try {
            fileInfo = saveFileInfo(userId, dirId, multipartFile);
        }
        catch (CommonException e) {
            return new MessageOutput<>(new State(e.getCode()));
        }

        // 如果是加密的pdf则返回错误信息, 等待上传密码
        if (FileStatusEnum.ENCRYPTED.getCode().equals(fileInfo.getStatus())) {
            return new MessageOutput<>(new State(ResultCodeEnum.PDF_REQUIRE_PASSWORD), fileInfo.getId());
        }

        MessageOutput<String> message = new MessageOutput<String>(new State(CommonConstant.SUCCESS_CODE),
                FileConstant.FILE_IMPORT_SUCCESS);
        message.setBody(JSONUtil.toStrByDefault(fileInfo));

        FormatTask task = new FormatTask();
        task.setDirId(dirId);
        task.setIp(ip);
        task.setUserId(userId);
        task.setMultipartFileList(multipartFile);
        task.setFileInfo(fileInfo);

        try (InputStream inputStream = multipartFile.getInputStream();) {
            task.setFileBytes(IOUtils.toByteArray(inputStream));
        }
        catch (Exception e) {
            logger.warn("读取上传文件错误", e);
        }
        formatStation.add(task);

        //增加埋点
        ArgusProperties argusProperties = BeanHelper.getArgusProperties();
        ArgusPostService argusPostService = BeanHelper.getArgusPostService();
        if (argusProperties.getBuryingEnable()) {
            String fileType = fileInfo.getFileType();
            switch (fileType) {
                case "pdf":
                    argusPostService.postAgent(ip, argusProperties.getOcrUploadPdfNum(), 1, "userId=" + userId, null);
                    break;
                case "png":
                    argusPostService.postAgent(ip, argusProperties.getOcrUploadPngNum(), 1, "userId=" + userId, null);
                    break;
                case "jpg":
                    argusPostService.postAgent(ip, argusProperties.getOcrUploadJpgNum(), 1, "userId=" + userId, null);
                    break;
                case "jpeg":
                    argusPostService.postAgent(ip, argusProperties.getOcrUploadJpegNum(), 1, "userId=" + userId, null);
                    break;
                case "bmp":
                    argusPostService.postAgent(ip, argusProperties.getOcrUploadBmpNum(), 1, "userId=" + userId, null);
                    break;
                case "tif":
                    argusPostService.postAgent(ip, argusProperties.getOcrUploadTifNum(), 1, "userId=" + userId, null);
                    break;
                case "tiff":
                    argusPostService.postAgent(ip, argusProperties.getOcrUploadTiffNum(), 1, "userId=" + userId, null);
                    break;
                default:
                    break;
            }
        }

        //先存储起来
        return message;
    }

    /**
     * 上传并保存文件信息
     *
     * @param userId        用户id
     * @param dirId         目录id
     * @param multipartFile 文件
     *
     * @return 文件信息
     */
    private FileInfo saveFileInfo(String userId, String dirId, MultipartFile multipartFile) {
        try {
            FileService fileService = BeanHelper.getFileServiceBean();

            long fileSize = multipartFile.getSize();
            String fileName = multipartFile.getOriginalFilename();
            String originalFileType = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length());
            String fileType = originalFileType.toLowerCase();

            // 保存文件信息
            FileInfo fileInfo = new FileInfo();
            fileInfo.setDirId(dirId);
            fileInfo.setFileName(fileName.substring(0, fileName.lastIndexOf(".")));
            fileInfo.setFileType(originalFileType);
            fileInfo.setFileSize(fileSize);
            fileInfo.setUserId(userId);

            if ("pdf".equalsIgnoreCase(fileType)) {
                try (InputStream fis = multipartFile.getInputStream()) {
                    // pdf文件，设置页数
                    logger.debug("------- 获取pdf总页数-------");
                    int pageCount = ImageUtil.getPdfPageCount(multipartFile.getInputStream());

                    fileInfo.setFilePage(pageCount);

                    int remainRecognizePageSize = BeanHelper.getRedisService().getRemainRecognizePageSize(userId);
                    if (remainRecognizePageSize != -1 && remainRecognizePageSize < pageCount) {
                        throw new CommonException(ResultCodeEnum.RECOGNIZE_PAGE_EXCEED_LIMIT);
                    }

                    // 文件信息上传到fdfs
                    String filePath = fileService.uploadFile(multipartFile);
                    fileInfo.setFilePath(filePath);

                    // 判断文件是否需要密码
                    byte[] bytes = IOUtils.toByteArray(fis);
                    PDDocument doc = PDDocument.load(new ByteArrayInputStream(bytes));
                    if (doc.isEncrypted()) {
                        fileInfo.setStatus(FileStatusEnum.ENCRYPTED.getCode());
                        String fileId = fileService.saveFileInfo(fileInfo);
                        fileInfo.setId(fileId);
                        return fileInfo;
                    }
                }
                catch (InvalidPasswordException exception) {
                    fileInfo.setStatus(FileStatusEnum.ENCRYPTED.getCode());
                    String fileId = fileService.saveFileInfo(fileInfo);
                    fileInfo.setId(fileId);
                    return fileInfo;
                }
            }
            else {
                // 文件信息上传到fdfs
                String filePath = fileService.uploadFile(multipartFile);
                fileInfo.setFilePath(filePath);
                // 普通文件，设置页数默认1
                fileInfo.setFilePage(1);

                // 更新识别页数统计
                BeanHelper.getRedisService().increamentRecognizePageCount(userId, 1);
            }

            if ("jpg".equalsIgnoreCase(fileType) || "jpeg".equalsIgnoreCase(fileType) ||
                    "png".equalsIgnoreCase(fileType) || "bmp".equalsIgnoreCase(fileType)) {
                if (fileSize > CommonConstant.FORMAT_MIN_SIZE) {
                    // 压缩图片文件
                    logger.debug("------- 开始图片压缩-------");

                    double rate = (double) CommonConstant.FORMAT_MIN_SIZE / (double) fileSize;
                    // try (ByteArrayOutputStream reduceStream = ImageUtil.reduceImgByRate(multipartFile.getInputStream(), rate)) {
                    // try (ByteArrayOutputStream reduceStream = ImageUtil.cutImage(multipartFile.getInputStream(), rate, fileName)) {
                    try (ByteArrayOutputStream reduceStream = ImageUtil.reduceImgByThumbnailsRate(multipartFile.getInputStream(), rate)) {
                        if (reduceStream != null && reduceStream.size() > 0) {
                            byte[] bytes = reduceStream.toByteArray();
                            long formatSize = bytes.length;
                            String formatPath = fileService.uploadFile(fileName, "jpg", bytes);
                            bytes = null;
                            reduceStream.close();

                            // 更新格式化信息
                            fileInfo.setFormatType("jpg");
                            fileInfo.setFormatSize(formatSize);
                            fileInfo.setFormatPath(formatPath);
                        }
                    }
                }
            }

            if ("tif".equals(fileType) || "tiff".equals(fileType)) {
                // 格式转换
                logger.debug("------- 开始图片格式转换-------");
                ByteArrayOutputStream jpgStream = (ByteArrayOutputStream) ImageUtil
                        .tiff2Jpg(multipartFile.getInputStream());
                if (jpgStream != null) {
                    byte[] bytes = jpgStream.toByteArray();
                    long formatSize = bytes.length;
                    String formatPath = fileService.uploadFile(fileName, "jpg", bytes);
                    bytes = null;
                    jpgStream.close();

                    // 更新格式化信息
                    fileInfo.setFormatType("jpg");
                    fileInfo.setFormatSize(formatSize);
                    fileInfo.setFormatPath(formatPath);
                }
            }

            String fileId = fileService.saveFileInfo(fileInfo);
            // 更新文件对象中的id
            fileInfo.setId(fileId);
            return fileInfo;
        }
        catch (CommonException e) {
            throw e;
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 检查上传文件的类型和大小
     *
     * @return
     */
    private MessageOutput<String> checkUploadFileTypAndSize(MultipartFile multipartFile, String userId) {
        // 校验文件格式
        String fileName = multipartFile.getOriginalFilename();
        if (!FileUtil.isValidUploadFile(fileName)) {
            return new MessageOutput<>(new State(CommonConstant.FAIL_CODE, "文件格式不支持"), fileName);
        }

        // 校验文件大小
        long fileSize = multipartFile.getSize();
        if (FileUtil.isValidUploadPic(fileName)) {
            // 校验识别页数限制
            int remainRecognizePageSize = BeanHelper.getRedisService().getRemainRecognizePageSize(userId);
            if (remainRecognizePageSize == 0) {
                return new MessageOutput<>(new State(ResultCodeEnum.RECOGNIZE_PAGE_EXCEED_LIMIT), fileName);
            }

            // 上传 图片文件大小超过限制
            if (fileSize > CommonConstant.MAX_UPLOAD_PIC_SIZE) {
                return new MessageOutput<>(new State(CommonConstant.FAIL_CODE, "文件大小超过限制"), fileName);
            }
        }
        else if (FileUtil.isSuffixEqualsIngnoreCase(fileName, "pdf") && fileSize > CommonConstant.MAX_UPLOAD_PDF_SIZE) {
            // 上传的 pdf 文件大小超过限制
            return new MessageOutput<>(new State(CommonConstant.FAIL_CODE, "文件大小超过限制"), fileName);
        }

        return null;
    }
}
